package log;

import common.FileMapping;

import java.io.*;
import java.nio.channels.FileChannel;
import java.nio.charset.StandardCharsets;
import java.util.ArrayList;
import java.util.LinkedList;
import java.util.logging.Logger;

public class LogFileStream implements ILogSteam
{
    @Override
    public LogItem read() {

        if (!_logList.isEmpty()) {
            return _logList.pop();
        }

        if (_state == State_Closed) {
            return null;
        } else if (_state == State_None) {
            if (!_openFile()) {
                return null;
            }
        }

        // read more log from file
        while (_logList.isEmpty() && _state == State_Open) {
            if (!readFile()) {
                _state = State_Closed;
                return null;
            }
        }

        // may or may not state is closed
        if (_logList.isEmpty()) {
            return null;
        }
        else {
            return _logList.pop();
        }
    }

    @Override
    public void close() {
        _closeFile();
    }

    public LogFileStream(String filePath, LogParser parser) {
        this._filePath = filePath;
        this._parser = parser;

        parser.setCallback(new LogParser.ILogParserCallback() {
            @Override
            public void OnLogItem(LogItem item) {
                _logList.push(item);
            }
        });
    }

    public boolean readFile()
    {
        try {
            String line = _bufferReader.readLine();
            if (line == null) {
                // reaches end of file
                _parser.flush();
                _state = State_Closed;
            }
            else {
                _parser.parse(line);
            }
            return true;
        }
        catch (Exception e) {
            Logger.getGlobal().info("failed to parse log file, " + e.getMessage());
            _closeFile();
            return false;
        }
    }

    private boolean _openFile() {
        try {
            if (_state != State_None) {
                Logger.getGlobal().info(String.format("failed to open log file, invalid state, path=%s, state=%d", _filePath, _state));
                return false;
            }

            _fileStream = new FileInputStream(_filePath);
            _streamReader = new InputStreamReader(_fileStream, StandardCharsets.UTF_8);
            _bufferReader = new BufferedReader(_streamReader);
            _state = State_Open;
            return true;
        }
        catch (Exception e) {
            Logger.getGlobal().info(String.format("failed to open log file, path=%s, msg=%s", _filePath, e.getMessage()));
            _state = State_Closed;
            return false;
        }
    }

    private void _closeFile() {
        try {
            if (_bufferReader != null) {
                _bufferReader.close();
            }
            if (_streamReader != null) {
                _streamReader.close();
            }
            if (_fileStream != null) {
                _fileStream.close();
            }
        }
        catch (Exception e) {
            Logger.getGlobal().info(String.format("failed to open log file, path=%s, msg=%s", _filePath, e.getMessage()));
        }
        finally {
            _state = State_Closed;
            _bufferReader = null;
            _streamReader = null;
            _fileStream = null;
        }
    }


    private static final int State_None = 0;
    private static final int State_Open = 1;
    private static final int State_Closed = 2;


    private String _filePath;
    private FileInputStream _fileStream;
    private InputStreamReader _streamReader;
    private BufferedReader _bufferReader;

    private int _state = State_None;
    private LogParser _parser;
    private final LinkedList<LogItem> _logList = new LinkedList<>();
}
